#include "glRender.h"
#include "../rtCommon/Intersect.h"

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

// -----------------------------------------------------------------------------------------
// ----------------- glRender for Triangle
// -----------------------------------------------------------------------------------------
void glRenderTriangle       (const float4 & v0, 
														 const float4 & v1, 
														 const float4 & v2, 
														 const float3 & clr)
{
	glBegin(GL_TRIANGLES);
	{
		glColor3f(clr.x, clr.y, clr.z);

		glVertex3f(v0.x, v0.y, v0.z);
		glVertex3f(v1.x, v1.y, v1.z);
		glVertex3f(v2.x, v2.y, v2.z);
	}
	glEnd();
}

void glRenderTriangle       (const float3 & v0, 
														 const float3 & v1, 
														 const float3 & v2, 
														 const float3 & clr)
{
	glBegin(GL_TRIANGLES);
	{
		glColor3f(clr.x, clr.y, clr.z);

		glVertex3f(v0.x, v0.y, v0.z);
		glVertex3f(v1.x, v1.y, v1.z);
		glVertex3f(v2.x, v2.y, v2.z);
	}
	glEnd();
}


// -----------------------------------------------------------------------------------------
// ----------------- glRender for Mesh
// -----------------------------------------------------------------------------------------
void glRender(CglMesh & mesh, bool bModelSpace)
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
		if (bModelSpace) glMultMatrixf( (GLfloat *) &mesh.matrixWS );

    if (mesh.pos.size()    == 0 || 
        mesh.posIdx.size() == 0)
        return;

    if (mesh.program != NULL)
    {
        mesh.program->Enable();
    }

    //glMaterialfv( GL_FRONT, GL_AMBIENT,   (GLfloat *) &mesh.material.ambient   );
    //glMaterialfv( GL_FRONT, GL_DIFFUSE,   (GLfloat *) &mesh.material.diffuse   );
    //glMaterialfv( GL_FRONT, GL_SPECULAR,  (GLfloat *) &mesh.material.specular  );
    //glMaterialfv( GL_FRONT, GL_EMISSION,  (GLfloat *) &mesh.material.emission  );
    //glMaterialfv( GL_FRONT, GL_SHININESS, (GLfloat *) &mesh.material.shininess );

		if (mesh.pos.size())
		{
			uint32 posO = mesh.pos.offsetR(); 
			uint32 posVBO = mesh.pos.vboR() ;

			if ( posVBO )
			//if (0)                                      // If VBO is enabled for positions
			{   
				glEnableClientState( GL_VERTEX_ARRAY );
				glBindBufferARB    ( GL_ARRAY_BUFFER_ARB, posVBO );
				glVertexPointer    ( 4, GL_FLOAT, 0, BUFFER_OFFSET(posO*sizeof(float4))); // Set The Vertex Pointer To The Vertex Buffer
			} 
			else
			{
				float4 * pPos = mesh.pos.hPtrR();

				glEnableClientState( GL_VERTEX_ARRAY );
				glVertexPointer    ( 4, GL_FLOAT, 0, (void *) &pPos[posO] );   // Set The Vertex Pointer To The Vertex Data
			}
		}

    if (mesh.nml.size())
    {
			uint32 nlmO = mesh.nml.offsetR();
			uint32 nlmVBO = mesh.nml.vboR();
      
			if ( nlmVBO )
      //if (0)
      {
          glEnableClientState( GL_NORMAL_ARRAY );
          glBindBufferARB    ( GL_ARRAY_BUFFER_ARB, nlmVBO );
          glNormalPointer    ( GL_FLOAT, sizeof(float4), BUFFER_OFFSET(nlmO*sizeof(float4)));           // normals are stored in float4 instead of float3
      }
      else
      {
				float4 * pNlm = mesh.nml.hPtrR(); 
 
        glEnableClientState( GL_NORMAL_ARRAY); 
        glNormalPointer    ( GL_FLOAT, sizeof(float4), (void *) &pNlm[nlmO] ); // normals are stored in float4 instead of float3
      }
    }

    if (mesh.clr.size())
    {
        uint32 clrVBO = mesh.clr.vboR();
        uint32 clrO   = mesh.clr.offsetR();
				
				uint32 clrIdxVBO = mesh.clrIdx.vboR();
				uint32 clrIdxO   = mesh.clrIdx.offsetR();
				        
				if (clrVBO && clrIdxVBO )        
				//if (0)
        {
          glEnableClientState( GL_COLOR_ARRAY );
          glBindBufferARB    ( GL_ARRAY_BUFFER_ARB,  clrVBO );
          glColorPointer     ( 4, GL_FLOAT, 0,       BUFFER_OFFSET(clrO*sizeof(float4)) );  

          glEnableClientState( GL_INDEX_ARRAY );
          glBindBufferARB    ( GL_ARRAY_BUFFER_ARB,  clrIdxVBO );
          glIndexPointer     ( GL_INT, sizeof(int4), BUFFER_OFFSET(clrIdxO*sizeof(int4)) );        // idx are stored in int4 instead of int3
        }
        else
        {        
					float4 * pClr = mesh.clr.hPtrR();
					int4   * pClrIdx = mesh.clrIdx.hPtrR();

          glEnableClientState( GL_COLOR_ARRAY); 
          glColorPointer     ( 4, GL_FLOAT, 0,       (void *) &pClr[clrO] ); 
          glEnableClientState( GL_INDEX_ARRAY);
          glIndexPointer     ( GL_INT, sizeof(int4), (void *) &pClrIdx[clrIdxO] );    // idx are stored in int4 instead of int3
        }
    }

    if (mesh.uv.size())
    {
      uint32 uvVBO = mesh.uv.vboR();
			uint32 uvO = mesh.uv.offsetR();

      if ( uvVBO )
			//if (0)
      {
				glEnableClientState( GL_TEXTURE_COORD_ARRAY);
        glBindBufferARB    ( GL_ARRAY_BUFFER_ARB, uvVBO );
        glTexCoordPointer  ( 2, GL_FLOAT, 0, BUFFER_OFFSET(uvO*sizeof(float2)) );
      }
      else
      {
				float2 * pUV = mesh.uv.hPtrR();

        glEnableClientState( GL_TEXTURE_COORD_ARRAY); 
        glTexCoordPointer  ( 2, GL_FLOAT, 0, (void *) &pUV[uvO] );
      }
    }

		if (mesh.posIdx.size())
		{
			uint32 posIdxVBO = mesh.posIdx.vboR();
			uint32 posIdxO   = mesh.posIdx.offsetR();	

			if (posIdxVBO)
			//if (0)
			{	
				glBindBufferARB    ( GL_ELEMENT_ARRAY_BUFFER_ARB, posIdxVBO ); 
				glDrawElements(GL_TRIANGLES, mesh.posIdx.size(), GL_UNSIGNED_INT, BUFFER_OFFSET(posIdxO*sizeof(int)) ); 
			}
			else
			{
				int * pPosIdx = mesh.posIdx.hPtrR();
				glDrawElements(GL_TRIANGLES, mesh.posIdx.size(), GL_UNSIGNED_INT, (void *) &pPosIdx[posIdxO] ); 
			}
		}

    if (mesh.nml.size())
        glDisableClientState( GL_NORMAL_ARRAY ); 

    if (mesh.clr.size())
    {
        glDisableClientState( GL_COLOR_ARRAY ); 
        glDisableClientState( GL_INDEX_ARRAY ); 
    }

    if (mesh.uv.size())
        glDisableClientState( GL_TEXTURE_COORD_ARRAY ); 

    if (mesh.program != NULL)
    {
        mesh.program->Disable();
    }

    glPopMatrix();
}

// -----------------------------------------------------------------------------------------
// ----------------- glRender for Xobj
// -----------------------------------------------------------------------------------------

void glRender(const CXobjIO & xobj)
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMultMatrixf( (GLfloat *) &xobj.matrixWS);

    glEnableClientState(GL_VERTEX_ARRAY);    
    glVertexPointer( 4, GL_FLOAT, 0, &xobj.pos[0] ); 

    if (xobj.nml.size())
    {
        glEnableClientState(GL_NORMAL_ARRAY); 
        glNormalPointer( GL_FLOAT, sizeof(float4), &xobj.nml[0] ); 
    }

    if (xobj.clr.size())
    {
        glEnableClientState(GL_COLOR_ARRAY); 
        glColorPointer( 4, GL_FLOAT, sizeof(float4), &xobj.clr[0] ); 
        glEnableClientState(GL_INDEX_ARRAY);
        glIndexPointer(GL_INT, sizeof(int4), &xobj.clrIdx[0]);
    }

    if (xobj.uv.size())
    {
        glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
        glTexCoordPointer( 2, GL_FLOAT, 0, &xobj.uv[0] ); 
    }

    glDrawElements(GL_TRIANGLES, xobj.posIdx.size(), GL_UNSIGNED_INT, &xobj.posIdx[0]);    

    if (xobj.nml.size())
        glDisableClientState(GL_NORMAL_ARRAY); 

    if (xobj.clr.size())
    {
        glDisableClientState(GL_COLOR_ARRAY); 
        glDisableClientState(GL_INDEX_ARRAY); 
    }

    if (xobj.uv.size())
        glDisableClientState(GL_TEXTURE_COORD_ARRAY); 

    glPopMatrix();
}


void glRenderFaceNormals(const CXobjIO & xobj)
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    //glMultMatrixf( (GLfloat *) &xobj.matrixWS);

    glBegin(GL_LINES);
    for (uint32 iFace = 0; iFace < xobj.posIdx.size(); iFace += 3)
    {
        int3 idx = { xobj.posIdx[iFace + 0], 
                     xobj.posIdx[iFace + 1], 
                     xobj.posIdx[iFace + 2] };

        float3 v0 = xyz(xobj.matrixWS * xobj.pos[idx.x]);
        float3 v1 = xyz(xobj.matrixWS * xobj.pos[idx.y]);
        float3 v2 = xyz(xobj.matrixWS * xobj.pos[idx.z]);

        float3 normal = normalize(cross(v2 - v0, v1 - v0));
        float  normalLength = 1.0f;

        float3 v3 = v0 + normal * normalLength;
        float3 v4 = v1 + normal * normalLength;
        float3 v5 = v2 + normal * normalLength;

        glColor3f(1, 1, 1);
        glVertex4fv( (GLfloat *) &v0);
        glVertex4fv( (GLfloat *) &v3);

        glColor3f(1, 1, 1);
        glVertex4fv( (GLfloat *) &v1);
        glVertex4fv( (GLfloat *) &v4);

        glColor3f(1, 1, 1);
        glVertex4fv( (GLfloat *) &v2);
        glVertex4fv( (GLfloat *) &v5);
    }

    glEnd();

    glPopMatrix();
}

void glRenderAABB(const CXobjIO & xobj)
{
    float3 clrAABB[4] = {
        { 1.0f, 1.0f, 1.0f } ,
        { 1.0f, 0.0f, 0.0f } ,
        { 0.0f, 0.0f, 0.0f } ,
        { 0.0f, 0.0f, 1.0f } 
    };

    glRender(xobj.m_AABB, clrAABB);
}

// -----------------------------------------------------------------------------------------
// ----------------- glRender for Geometry Frustum
// -----------------------------------------------------------------------------------------

void glRender(const SGeometryFrustum & geomFrustum)
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    glEnableClientState(GL_VERTEX_ARRAY);    
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_INDEX_ARRAY);   

    glVertexPointer( 3, GL_FLOAT, 0, &geomFrustum.m_Vertex[0] ); 
    glColorPointer( 3, GL_FLOAT,  0, &geomFrustum.m_Color[0]  ); 
    glIndexPointer(GL_INT,        0, &geomFrustum.m_ClrIdx[0] );

    glDrawElements(GL_TRIANGLE_FAN, 6, GL_UNSIGNED_INT, &geomFrustum.m_VtxIdx[0]);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY); 
    glDisableClientState(GL_INDEX_ARRAY); 

    glPopMatrix();
}

// -----------------------------------------------------------------------------------------
// ----------------- glRender for Box
// -----------------------------------------------------------------------------------------

void glRender(const S_Box & box, float3 * pClr)
{
    glBegin(GL_TRIANGLES); // quick debug code

    glColor3fv((GLfloat *) &pClr[0]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glColor3fv((GLfloat *) &pClr[1]);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z);

    glColor3fv((GLfloat *) &pClr[0]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glColor3fv((GLfloat *) &pClr[1]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z); //---------------------

    glColor3fv((GLfloat *) &pClr[0]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glColor3fv((GLfloat *) &pClr[1]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z);

    glColor3fv((GLfloat *) &pClr[0]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glColor3fv((GLfloat *) &pClr[1]);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z); //---------------------

    glColor3fv((GLfloat *) &pClr[0]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glColor3fv((GLfloat *) &pClr[1]);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);

    glColor3fv((GLfloat *) &pClr[0]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glColor3fv((GLfloat *) &pClr[1]);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z); //---------------------

    glColor3fv((GLfloat *) &pClr[2]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glColor3fv((GLfloat *) &pClr[3]);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);

    glColor3fv((GLfloat *) &pClr[2]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glColor3fv((GLfloat *) &pClr[3]);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z); //---------------------

    glColor3fv((GLfloat *) &pClr[2]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glColor3fv((GLfloat *) &pClr[3]);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z);

    glColor3fv((GLfloat *) &pClr[2]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glColor3fv((GLfloat *) &pClr[3]);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z); //---------------------

    glColor3fv((GLfloat *) &pClr[2]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glColor3fv((GLfloat *) &pClr[3]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z);

    glColor3fv((GLfloat *) &pClr[2]);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glColor3fv((GLfloat *) &pClr[3]);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z);

    glEnd();
}

void glRender(const S_Box & box, const float3 & clr)
{
    glBegin(GL_TRIANGLES); // quick debug code

    glColor3fv((GLfloat *) &clr);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z);

    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z); //---------------------

    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z);

    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z); //---------------------

    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);

    glVertex3f(box.pmin.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z); //---------------------

    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);

    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmin.z);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z); //---------------------

    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmax.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z);

    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmin.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z); //---------------------

    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmax.x, box.pmax.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z);

    glVertex3f(box.pmax.x, box.pmax.y, box.pmax.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmin.z);
    glVertex3f(box.pmin.x, box.pmax.y, box.pmax.z);

    glEnd();
}

void glRender(const S_Box & box, const matrix4x4 & M4x4, float3 * pClr)
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMultMatrixf( (GLfloat *) &M4x4);

    glRender(box, pClr);

    glPopMatrix();
}

//// -----------------------------------------------------------------------------------------
//// ----------------- glRender for Regular Grid
//// -----------------------------------------------------------------------------------------
//
//void glRender(const SDistanceField2 & grid, const SAABox & aabb, bool bRenderSpace = false)
//{
//    float3 clrRed[4] = {
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } };
//
//    float3 clrBlue[4] = {
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } };
//
//        float3 f3whd = aabb.pmax - aabb.pmin;
//        float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
//        float3 f3whdSmall = f3whd * invGridDim;
//
//        float fwhdSmall = length(f3whdSmall);
//
//        SAABox aabb_small;
//
//        aabb_small.pmin = aabb.pmin;
//        aabb_small.pmax = aabb.pmin + f3whdSmall;
//
//        for (uint32 ix = 0; ix < grid.m_Sx; ix++)
//        {
//            aabb_small.pmin.y = aabb.pmin.y;
//            aabb_small.pmax.y = aabb.pmin.y + f3whdSmall.y;
//            for (uint32 iy = 0; iy < grid.m_Sy; iy++)
//            {
//                aabb_small.pmin.z = aabb.pmin.z;
//                aabb_small.pmax.z = aabb.pmin.z + f3whdSmall.z;
//                for (uint32 iz = 0; iz < grid.m_Sz; iz++)
//                {
//                    if (abs(Get( (SDistanceField2 *) &grid, ix, iy, iz, 1)) > 0 )
//                        glRender(aabb_small, clrRed); 
//                    else
//                    {
//                        if (bRenderSpace)
//                            glRender(aabb_small, clrBlue);
//                    }
//
//                    aabb_small.pmin.z += f3whdSmall.z;
//                    aabb_small.pmax.z += f3whdSmall.z;          
//                }
//                aabb_small.pmin.y += f3whdSmall.y;
//                aabb_small.pmax.y += f3whdSmall.y; 
//            }
//            aabb_small.pmin.x += f3whdSmall.x;
//            aabb_small.pmax.x += f3whdSmall.x; 
//        }
//}


// -----------------------------------------------------------------------------------------
// ----------------- glRender for Regular Grid
// -----------------------------------------------------------------------------------------

//void glRender(const SDistanceField & grid, const SAABox & aabb, bool bRenderSpace = false)
//{
//    float3 clrRed[4] = {
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } };
//
//    float3 clrBlue[4] = {
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } };
//
//        float3 f3whd = aabb.pmax - aabb.pmin;
//        float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
//        float3 f3whdSmall = f3whd * invGridDim;
//
//        float fwhdSmall = length(f3whdSmall);
//
//        SAABox aabb_small;
//
//        aabb_small.pmin = aabb.pmin;
//        aabb_small.pmax = aabb.pmin + f3whdSmall;
//
//        for (uint32 ix = 0; ix < grid.m_Sx; ix++)
//        {
//            aabb_small.pmin.y = aabb.pmin.y;
//            aabb_small.pmax.y = aabb.pmin.y + f3whdSmall.y;
//            for (uint32 iy = 0; iy < grid.m_Sy; iy++)
//            {
//                aabb_small.pmin.z = aabb.pmin.z;
//                aabb_small.pmax.z = aabb.pmin.z + f3whdSmall.z;
//                for (uint32 iz = 0; iz < grid.m_Sz; iz++)
//                {
//                    if (abs(Get( (SDistanceField *) &grid, ix, iy, iz)) < fwhdSmall * 0.5f )
//                        glRender(aabb_small, clrRed); 
//                    else
//                    {
//                        if (bRenderSpace)
//                            glRender(aabb_small, clrBlue);
//                    }
//
//                    aabb_small.pmin.z += f3whdSmall.z;
//                    aabb_small.pmax.z += f3whdSmall.z;          
//                }
//                aabb_small.pmin.y += f3whdSmall.y;
//                aabb_small.pmax.y += f3whdSmall.y; 
//            }
//            aabb_small.pmin.x += f3whdSmall.x;
//            aabb_small.pmax.x += f3whdSmall.x; 
//        }
//}
//
//void glRenderCenter(const SDistanceField & grid, const SAABox & aabb)
//{
//    float3 f3whd = aabb.pmax - aabb.pmin;
//    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
//    float3 f3whdSmall = f3whd * invGridDim;
//
//    float fwhdSmall = length(f3whdSmall);
//
//    glBegin(GL_POINTS);
//
//    // go from pmin to pmax
//    // shift into the center of first voxel cube
//    float3 pmin = aabb.pmin + f3whdSmall * 0.5f; 
//
//    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
//    {
//        pmin.y = aabb.pmin.y + f3whdSmall.y * 0.5f;
//        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
//        {
//            pmin.z = aabb.pmin.z + f3whdSmall.z * 0.5f;
//            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
//            {        
//                if (Get( (SDistanceField *) &grid, ix, iy, iz) < EPSILON )
//                {
//                    glColor3f(1, 1, 0);
//                    glVertex3f(pmin.x, pmin.y, pmin.z);
//                }
//                else
//                {
//                    glColor3f(1, 0, 1);
//                    glVertex3f(pmin.x, pmin.y, pmin.z);
//                }
//                pmin.z += f3whdSmall.z;                       
//            }
//            pmin.y += f3whdSmall.y;
//        }
//        pmin.x += f3whdSmall.x; 
//    }
//
//    glEnd();
//}
//
//// -----------------------------------------------------------------------------------------
//// ----------------- glRender for Particle Emitter
//// -----------------------------------------------------------------------------------------
//void glRender(const SEmitter   & emitter, 
//              const SFloatGrid & grid, 
//              const SFloatGrid & u,
//              const SFloatGrid & v, 
//              const SFloatGrid & w,
//              const SAABox     & aabb)
//{
//
//    glPointSize(20.0f);
//    glBegin(GL_POINTS);
//
//    glColor3f(1, 1, 0);
//    glVertex3fv( (GLfloat *) &emitter.pos );
//
//    glEnd();
//    glPointSize(1.0f);
//
//    glBegin(GL_LINES);
//
//    glColor3f(1, 1, 1);
//    glVertex3fv( (GLfloat *) &emitter.pos );
//    float3 dir = emitter.pos + emitter.velocity * 10;
//    glVertex3fv( (GLfloat *) &dir );
//
//    glEnd();
//
//    float3 clrRed[4] = {
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } };
//
//        float3 clrWhite[4] = {
//            { 1.0f, 1.0f, 1.0f } ,
//            { 1.0f, 1.0f, 1.0f } ,
//            { 1.0f, 1.0f, 1.0f } ,
//            { 1.0f, 1.0f, 1.0f } };
//
//            float3 clrBlue[4] = {
//                { 0.0f, 0.0f, 1.0f } ,
//                { 0.0f, 0.0f, 1.0f } ,
//                { 0.0f, 0.0f, 1.0f } ,
//                { 0.0f, 0.0f, 1.0f } };
//
//                float3 f3whd = aabb.pmax - aabb.pmin;
//                float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
//                float3 f3whdSmall = f3whd * invGridDim;
//
//                float fwhdSmall = length(f3whdSmall);
//
//                SAABox aabb_small;
//
//                aabb_small.pmin = aabb.pmin;
//                aabb_small.pmax = aabb.pmin + f3whdSmall; 
//
//                glBegin(GL_LINES);
//
//                for (uint32 ix = 0; ix < grid.m_Sx; ix++)
//                {
//                    aabb_small.pmin.y = aabb.pmin.y;
//                    aabb_small.pmax.y = aabb.pmin.y + f3whdSmall.y;
//                    for (uint32 iy = 0; iy < grid.m_Sy; iy++)
//                    {
//                        aabb_small.pmin.z = aabb.pmin.z;
//                        aabb_small.pmax.z = aabb.pmin.z + f3whdSmall.z;
//                        for (uint32 iz = 0; iz < grid.m_Sz; iz++)
//                        {
//                            float3 p  = aabb_small.pmin;
//                            float3 pv = (to_float3( Get( (SDistanceField *) &u, ix, iy, iz), 
//                                Get( (SDistanceField *) &v, ix, iy, iz), 
//                                Get( (SDistanceField *) &w, ix, iy, iz) ));
//                            if (length(pv) > 0.01)
//                                pv = normalize(pv);
//
//                            float3 pp = p + pv * 4;
//
//                            glColor3f(1, 0, 0);
//                            glVertex3fv( (GLfloat * ) &p   );
//                            glColor3f(0, 0, 1);
//                            glVertex3fv( (GLfloat * ) &pp );
//
//                            //if (abs(Get(&grid, ix, iy, iz)) > 0.1f)
//                            //{
//                            //    float3 clrSmoke[4] = {
//                            //        { 1.0f, 1.0f, 1.0f } ,
//                            //        { 1.0f, 1.0f, 1.0f } ,
//                            //        { 1.0f, 1.0f, 1.0f } ,
//                            //        { 1.0f, 1.0f, 1.0f } };
//
//                            //    clrSmoke[0] = clrSmoke[0] * abs(Get(&grid, ix, iy, iz));
//                            //    clrSmoke[1] = clrSmoke[1] * abs(Get(&grid, ix, iy, iz));
//                            //    clrSmoke[2] = clrSmoke[2] * abs(Get(&grid, ix, iy, iz));
//                            //    clrSmoke[3] = clrSmoke[3] * abs(Get(&grid, ix, iy, iz));
//
//                            //    glRender(aabb_small, clrSmoke ); 
//                            //}
//
//                            aabb_small.pmin.z += f3whdSmall.z;
//                            aabb_small.pmax.z += f3whdSmall.z;          
//                        }
//                        aabb_small.pmin.y += f3whdSmall.y;
//                        aabb_small.pmax.y += f3whdSmall.y; 
//                    }
//                    aabb_small.pmin.x += f3whdSmall.x;
//                    aabb_small.pmax.x += f3whdSmall.x; 
//                } 
//
//                glEnd();
//}
//
//void glRender(const SEmitter    & emitter, 
//              const SFloatGrid2 & grid, 
//              const SFloatGrid2 & u,
//              const SFloatGrid2 & v, 
//              const SFloatGrid2 & w,
//              const SAABox      & aabb)
//{
//
//    glPointSize(20.0f);
//    glBegin(GL_POINTS);
//
//    glColor3f(1, 1, 0);
//    glVertex3fv( (GLfloat *) &emitter.pos );
//
//    glEnd();
//    glPointSize(1.0f);
//
//    glBegin(GL_LINES);
//
//    glColor3f(1, 1, 1);
//    glVertex3fv( (GLfloat *) &emitter.pos );
//    float3 dir = emitter.pos + emitter.velocity * 10;
//    glVertex3fv( (GLfloat *) &dir );
//
//    glEnd();
//
//    float3 clrRed[4] = {
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } };
//
//    float3 clrWhite[4] = {
//        { 1.0f, 1.0f, 1.0f } ,
//        { 1.0f, 1.0f, 1.0f } ,
//        { 1.0f, 1.0f, 1.0f } ,
//        { 1.0f, 1.0f, 1.0f } };
//
//    float3 clrBlue[4] = {
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } };
//
//    float3 f3whd = aabb.pmax - aabb.pmin;
//    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
//    float3 f3whdSmall = f3whd * invGridDim;
//
//    float fwhdSmall = length(f3whdSmall);
//
//    SAABox aabb_small;
//
//    aabb_small.pmin = aabb.pmin;
//    aabb_small.pmax = aabb.pmin + f3whdSmall; 
//
//    glBegin(GL_LINES);
//
//    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
//    {
//        aabb_small.pmin.y = aabb.pmin.y;
//        aabb_small.pmax.y = aabb.pmin.y + f3whdSmall.y;
//        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
//        {
//            aabb_small.pmin.z = aabb.pmin.z;
//            aabb_small.pmax.z = aabb.pmin.z + f3whdSmall.z;
//            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
//            {
//                float3 p  = aabb_small.pmin;
//                float3 pv = (to_float3( Get( (SDistanceField2 *) &u, ix, iy, iz), 
//                    Get( (SDistanceField2 *) &v, ix, iy, iz), 
//                    Get( (SDistanceField2 *) &w, ix, iy, iz) ));
//                if (length(pv) > 0.01)
//                    pv = normalize(pv);
//
//                float3 pp = p + pv * 4;
//
//                glColor3f(1, 0, 0);
//                glVertex3fv( (GLfloat * ) &p   );
//                glColor3f(0, 0, 1);
//                glVertex3fv( (GLfloat * ) &pp );
//
//
//                //if (abs(Get(&grid, ix, iy, iz)) > 0.1f)
//                //{
//                //    float3 clrSmoke[4] = {
//                //        { 1.0f, 1.0f, 1.0f } ,
//                //        { 1.0f, 1.0f, 1.0f } ,
//                //        { 1.0f, 1.0f, 1.0f } ,
//                //        { 1.0f, 1.0f, 1.0f } };
//
//                //    clrSmoke[0] = clrSmoke[0] * abs(Get(&grid, ix, iy, iz));
//                //    clrSmoke[1] = clrSmoke[1] * abs(Get(&grid, ix, iy, iz));
//                //    clrSmoke[2] = clrSmoke[2] * abs(Get(&grid, ix, iy, iz));
//                //    clrSmoke[3] = clrSmoke[3] * abs(Get(&grid, ix, iy, iz));
//
//                //    glRender(aabb_small, clrSmoke ); 
//                //}
//
//                aabb_small.pmin.z += f3whdSmall.z;
//                aabb_small.pmax.z += f3whdSmall.z;          
//            }
//            aabb_small.pmin.y += f3whdSmall.y;
//            aabb_small.pmax.y += f3whdSmall.y; 
//        }
//        aabb_small.pmin.x += f3whdSmall.x;
//        aabb_small.pmax.x += f3whdSmall.x; 
//    } 
//
//    glEnd();
//}
//
//void glRender(const SFloatGrid2 & grid, 
//              const SFloatGrid2 & u,
//              const SFloatGrid2 & v, 
//              const SFloatGrid2 & w,
//              const SAABox      & aabb)
//{
//
//    float3 clrRed[4] = {
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } ,
//        { 1.0f, 0.0f, 0.0f } };
//
//    float3 clrWhite[4] = {
//        { 1.0f, 1.0f, 1.0f } ,
//        { 1.0f, 1.0f, 1.0f } ,
//        { 1.0f, 1.0f, 1.0f } ,
//        { 1.0f, 1.0f, 1.0f } };
//
//    float3 clrBlue[4] = {
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } ,
//        { 0.0f, 0.0f, 1.0f } };
//
//    float3 f3whd = aabb.pmax - aabb.pmin;
//    float3 invGridDim = {1.0f / grid.m_Sx, 1.0f / grid.m_Sy, 1.0f / grid.m_Sz};
//    float3 f3whdSmall = f3whd * invGridDim;
//
//    float fwhdSmall = length(f3whdSmall);
//
//    SAABox aabb_small;
//
//    aabb_small.pmin = aabb.pmin;
//    aabb_small.pmax = aabb.pmin + f3whdSmall; 
//
//    glBegin(GL_LINES);
//
//    for (uint32 ix = 0; ix < grid.m_Sx; ix++)
//    {
//        aabb_small.pmin.y = aabb.pmin.y;
//        aabb_small.pmax.y = aabb.pmin.y + f3whdSmall.y;
//        for (uint32 iy = 0; iy < grid.m_Sy; iy++)
//        {
//            aabb_small.pmin.z = aabb.pmin.z;
//            aabb_small.pmax.z = aabb.pmin.z + f3whdSmall.z;
//            for (uint32 iz = 0; iz < grid.m_Sz; iz++)
//            {
//                float3 p  = aabb_small.pmin;
//                float3 pv = (to_float3( Get( (SDistanceField2 *) &u, ix, iy, iz), 
//                                        Get( (SDistanceField2 *) &v, ix, iy, iz), 
//                                        Get( (SDistanceField2 *) &w, ix, iy, iz) ));
//                if (length(pv) > 0.01)
//                    pv = normalize(pv);
//
//                float3 pp = p + pv * 4;
//
//                glColor3f(1, 0, 0);
//                glVertex3fv( (GLfloat * ) &p   );
//                glColor3f(0, 0, 1);
//                glVertex3fv( (GLfloat * ) &pp );
//
//
//                //if (abs(Get(&grid, ix, iy, iz)) > 0.1f)
//                //{
//                //    float3 clrSmoke[4] = {
//                //        { 1.0f, 1.0f, 1.0f } ,
//                //        { 1.0f, 1.0f, 1.0f } ,
//                //        { 1.0f, 1.0f, 1.0f } ,
//                //        { 1.0f, 1.0f, 1.0f } };
//
//                //    clrSmoke[0] = clrSmoke[0] * abs(Get(&grid, ix, iy, iz));
//                //    clrSmoke[1] = clrSmoke[1] * abs(Get(&grid, ix, iy, iz));
//                //    clrSmoke[2] = clrSmoke[2] * abs(Get(&grid, ix, iy, iz));
//                //    clrSmoke[3] = clrSmoke[3] * abs(Get(&grid, ix, iy, iz));
//
//                //    glRender(aabb_small, clrSmoke ); 
//                //}
//
//                aabb_small.pmin.z += f3whdSmall.z;
//                aabb_small.pmax.z += f3whdSmall.z;          
//            }
//            aabb_small.pmin.y += f3whdSmall.y;
//            aabb_small.pmax.y += f3whdSmall.y; 
//        }
//        aabb_small.pmin.x += f3whdSmall.x;
//        aabb_small.pmax.x += f3whdSmall.x; 
//    } 
//
//    glEnd();
//}
 
void glRender(const SLight & light)
{
    glPointSize(5.0f);

    glBegin(GL_POINTS);
    glColor3f(light.diffuse.x, light.diffuse.y, light.diffuse.z);
    glVertex3f(light.pos.x, light.pos.y, light.pos.z);
    glEnd();

    glPointSize(1.0f);
}

 
void glRender(const SLight & light, float3 pos, float dist)
{
    glPointSize(5.0f);

    glBegin(GL_POINTS);
    glColor3f(light.diffuse.x, light.diffuse.y, light.diffuse.z);
    glVertex3f(light.pos.x, light.pos.y, light.pos.z);
    glEnd();

    glBegin(GL_LINES);
    glColor3f(1, 0, 0);
    glVertex3f(light.pos.x, light.pos.y, light.pos.z);
    glColor3f(0, 0, 1);
    glVertex3f(light.pos.x - light.dir.x, light.pos.y - light.dir.y, light.pos.z - light.dir.z);
    glEnd();

    glPointSize(1.0f);
}

 
void glRenderVector(const float3 & v)
{
    glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(v.x, v.y, v.z);
    glEnd();
}

 
void glRenderVector(const float3 & v, const float3 & p)
{
    glBegin(GL_LINES);
    glVertex3f(p.x, p.y, p.z);
    glVertex3f(p.x + v.x, p.y + v.y, p.z + p.z);
    glEnd();
}

 
void glRenderQuad()
{
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0);
    glTexCoord2f(1.0, 0.0); glVertex2f( 1.0, -1.0);
    glTexCoord2f(1.0, 1.0); glVertex2f( 1.0,  1.0);
    glTexCoord2f(0.0, 1.0); glVertex2f(-1.0,  1.0);
    glEnd();
}

void glRender(CglWorld * pScene, bool bModelSpace)
{
	if (pScene == NULL) return;

 	CglMesh * pMesh = pScene->pMesh();
	uint32    nMesh = pScene->nMesh();

	for (uint32 iMesh = 0; iMesh < nMesh; iMesh++)
	{
		glRender(pMesh[iMesh], true);
	}
}

void glRender(CglWorld * pScene)
{
	if (pScene == NULL) return;

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();

	if (pScene->pos.size()    == 0 || 
		  pScene->posIdx.size() == 0)
		return;

	if (pScene->program != NULL)
	{
		pScene->program->Enable();
	}

	//glMaterialfv( GL_FRONT, GL_AMBIENT,   (GLfloat *) &pScene->material.ambient   );
	//glMaterialfv( GL_FRONT, GL_DIFFUSE,   (GLfloat *) &pScene->material.diffuse   );
	//glMaterialfv( GL_FRONT, GL_SPECULAR,  (GLfloat *) &pScene->material.specular  );
	//glMaterialfv( GL_FRONT, GL_EMISSION,  (GLfloat *) &pScene->material.emission  );
	//glMaterialfv( GL_FRONT, GL_SHININESS, (GLfloat *) &pScene->material.shininess );

	if (pScene->pos.size())
	{
		uint32 posVBO = pScene->pos.vboW() ;

		if ( posVBO )
		//if (0)                                      // If VBO is enabled for positions
		{   
			glEnableClientState( GL_VERTEX_ARRAY );
			glBindBufferARB    ( GL_ARRAY_BUFFER_ARB, posVBO );
			glVertexPointer    ( 4, GL_FLOAT, 0, BUFFER_OFFSET(0)); // Set The Vertex Pointer To The Vertex Buffer
		} 
		else
		{
			float4 * pPos = pScene->pos.hPtrW();

			glEnableClientState( GL_VERTEX_ARRAY );
			glVertexPointer    ( 4, GL_FLOAT, 0, (void *) &pPos);   // Set The Vertex Pointer To The Vertex Data
		}
	}

	if (pScene->nml.size())
	{
		uint32 nlmVBO = pScene->nml.vboW();

		if ( nlmVBO )
		//if (0)
		{
			glEnableClientState( GL_NORMAL_ARRAY );
			glBindBufferARB    ( GL_ARRAY_BUFFER_ARB, nlmVBO );
			glNormalPointer    ( GL_FLOAT, sizeof(float4), BUFFER_OFFSET(0));           // normals are stored in float4 instead of float3
		}
		else
		{
			float4 * pNlm = pScene->nml.hPtrW(); 

			glEnableClientState( GL_NORMAL_ARRAY); 
			glNormalPointer    ( GL_FLOAT, sizeof(float4), (void *) pNlm ); // normals are stored in float4 instead of float3
		}
	}

	if (pScene->clr.size())
	{
		uint32 clrVBO = pScene->clr.vboR();
		uint32 clrIdxVBO = pScene->clrIdx.vboW();

		if (clrVBO && clrIdxVBO )        
		//if (0)
		{
			glEnableClientState( GL_COLOR_ARRAY );
			glBindBufferARB    ( GL_ARRAY_BUFFER_ARB,  clrVBO );
			glColorPointer     ( 4, GL_FLOAT, 0,       BUFFER_OFFSET(0) );  

			glEnableClientState( GL_INDEX_ARRAY );
			glBindBufferARB    ( GL_ARRAY_BUFFER_ARB,  clrIdxVBO );
			glIndexPointer     ( GL_INT, sizeof(int4), BUFFER_OFFSET(0) );        // idx are stored in int4 instead of int3
		}
		else
		{        
			float4 * pClr = pScene->clr.hPtrR();
			int4   * pClrIdx = pScene->clrIdx.hPtrW();

			glEnableClientState( GL_COLOR_ARRAY); 
			glColorPointer     ( 4, GL_FLOAT, 0,       (void *) pClr ); 
			glEnableClientState( GL_INDEX_ARRAY);
			glIndexPointer     ( GL_INT, sizeof(int4), (void *) pClrIdx );    // idx are stored in int4 instead of int3
		}
	}

	if (pScene->uv.size())
	{
		uint32 uvVBO = pScene->uv.vboR();

		if ( uvVBO )
		//if (0)
		{
			glEnableClientState( GL_TEXTURE_COORD_ARRAY);
			glBindBufferARB    ( GL_ARRAY_BUFFER_ARB, uvVBO );
			glTexCoordPointer  ( 2, GL_FLOAT, 0, BUFFER_OFFSET(0) );
		}
		else
		{
			float2 * pUV = pScene->uv.hPtrR();

			glEnableClientState( GL_TEXTURE_COORD_ARRAY); 
			glTexCoordPointer  ( 2, GL_FLOAT, 0, (void *) pUV );
		}
	}

	if (pScene->posIdx.size())
	{
		uint32 posIdxVBO = pScene->posIdx.vboW();

		if (posIdxVBO)
		//if (0)
		{	
			glBindBufferARB    ( GL_ELEMENT_ARRAY_BUFFER_ARB, posIdxVBO ); 
			glDrawElements(GL_TRIANGLES, pScene->posIdx.size(), GL_UNSIGNED_INT, BUFFER_OFFSET(0) ); 
		}
		else
		{
			int * pPosIdx = pScene->posIdx.hPtrW();
			glDrawElements(GL_TRIANGLES, pScene->posIdx.size(), GL_UNSIGNED_INT, (void *) pPosIdx ); 
		}
	}

	if (pScene->nml.size())
		glDisableClientState( GL_NORMAL_ARRAY ); 

	if (pScene->clr.size())
	{
		glDisableClientState( GL_COLOR_ARRAY ); 
		glDisableClientState( GL_INDEX_ARRAY ); 
	}

	if (pScene->uv.size())
		glDisableClientState( GL_TEXTURE_COORD_ARRAY ); 

	if (pScene->program != NULL)
	{
		pScene->program->Disable();
	}

	glPopMatrix();
}

void glRenderKdTree(KdNode * pKdNode, const S_Box & box, int maxLevel, int curLevel, int nodeIdx, bool empty)
{
	const float3 Blue[4] = { {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1} };
	
	if (curLevel < maxLevel)
	{
		// draw left
		KdNode node = pKdNode[nodeIdx];
		S_Box lbox = box;
		float split = node.Split();
		int   axis  = node.Axis();
		if (axis != TREE_LEAF)
		{
			((float*)&lbox.pmax)[axis] = split;
			int  lidx = nodeIdx+1;
			glRenderKdTree(pKdNode, lbox, maxLevel, curLevel+1, lidx, empty);
			// draw right
			S_Box rbox = box;
			((float*)&rbox.pmin)[axis] = split;
			int  ridx = node.right;
			glRenderKdTree(pKdNode, rbox, maxLevel, curLevel+1, ridx, empty);
		}
		else
		{
			KdNode node = pKdNode[nodeIdx];
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			if ((node.Count() != 0) || empty) glRender(box, (float3 *) Blue);
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		}
	}
	else
	{
		KdNode node = pKdNode[nodeIdx];
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		if ((node.Count() != 0) || empty) glRender(box, (float3 *) Blue);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
}

void glRenderKdTree(CSpatialTree * pKdTree, int maxLevel, bool empty)
{
	if (pKdTree == NULL) return;

	glRenderKdTree((KdNode *) pKdTree->pKdTree(), pKdTree->box(), maxLevel, 0, 0, empty) ;
}

void glRenderKdTreeLeaf(KdNode       * pKdNode, 
												const S_Box & box, 
												float4       * pVertex,
												int          * pFace,
												uint         * pPrimitive,
												int            maxLevel, 
												int            curLevel, 
												int            nodeIdx, 
												bool           empty)
{
	if (curLevel < maxLevel)
	{
		// draw left
		KdNode node = pKdNode[nodeIdx];
		S_Box lbox = box;
		float split = node.Split();
		int   axis  = node.Axis();
		if (axis != TREE_LEAF)
		{
			((float*)&lbox.pmax)[axis] = split;
			int  lidx = nodeIdx+1;
			glRenderKdTreeLeaf(pKdNode, 
												 lbox, 
												 pVertex, 
												 pFace, 
												 pPrimitive, 
												 maxLevel, 
												 curLevel+1, 
												 lidx, 
												 empty);
			// draw right
			S_Box rbox = box;
			((float*)&rbox.pmin)[axis] = split;
			int  ridx = node.right;
			glRenderKdTreeLeaf(pKdNode, 
				                 rbox, 
												 pVertex, 
												 pFace, 
												 pPrimitive, 
												 maxLevel, 
												 curLevel+1, 
												 ridx, 
												 empty);
		}
		else
		{
			KdNode node = pKdNode[nodeIdx];
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			if ((node.Count() != 0)) 
			{
				int prim_start = node.start;
				int prim_count = node.Count();

				for (int i_prim = 0; i_prim < prim_count; i_prim++)
				{
					int     primitive = pPrimitive[i_prim + prim_start];
					int3    face = { pFace[3*primitive], pFace[3*primitive+1], pFace[3*primitive+2] };
					float4  v[3] = { pVertex[face.x],    pVertex[face.y],      pVertex[face.z] };					

					glRenderTriangle(v[0], v[1], v[2], f3(0, 1, 0));
				}
			}
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		}
	}
	else
	{
		KdNode node = pKdNode[nodeIdx];
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		//if ((node.Count() != 0) || empty) glRender(box, f3(0, 0, 1));
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
}

void glRenderKdTreeLeaf(CSpatialTree * pKdTree, int maxLevel, bool empty)
{
	if (pKdTree == NULL) return;

	glRenderKdTreeLeaf((KdNode *) pKdTree->pKdTree(), 
		                  pKdTree->box(), 
											pKdTree->pVertex(),
											pKdTree->pFace(),
											pKdTree->pPrimitive(),
											maxLevel, 
											0, 0, 
											empty) ;
}


void glRenderBVHTree(BVHNode * pBVHNode, int maxLevel, int curLevel, int nodeIdx, bool empty)
{
	const float3 Blue[4] = { {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1} };
		
	if (curLevel < maxLevel)
	{
		BVHNode node = pBVHNode[nodeIdx];
		S_Box box;
		box.pmin = node.pmin;
		box.pmax = node.pmax;	

		int   axis  = node.Axis();

		if (axis != TREE_LEAF)
		{		
			// draw left
			int  lidx = nodeIdx+1;
			glRenderBVHTree(pBVHNode, maxLevel, curLevel+1, lidx, empty);
			// draw right
			int  ridx = node.right;
			glRenderBVHTree(pBVHNode, maxLevel, curLevel+1, ridx, empty);
		}
		else
		{
			BVHNode node = pBVHNode[nodeIdx];
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			if ((node.Count() != 0) || empty) glRender(box, (float3 *) Blue);
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		}
	}
	else
	{
		BVHNode node = pBVHNode[nodeIdx];
		S_Box box;
		box.pmin = node.pmin;
		box.pmax = node.pmax;	

		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		if ((node.Count() != 0) || empty) glRender(box, (float3 *) Blue);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
}

void glRenderBVHTree(CSpatialTree * pBVHTree, int maxLevel, bool empty)
{
	if (pBVHTree == NULL) return;

	glRenderBVHTree((BVHNode *) pBVHTree->pBVHTree(), maxLevel, 0, 0, empty) ;
}

void glRenderBVHTreeLeaf(BVHNode * pBVHNode, 												
												 float4  * pVertex,
												 int     * pFace,
												 uint    * pPrimitive,
												 int       maxLevel, 
												 int       curLevel, 
												 int       nodeIdx, 
												 bool      empty)
{
	const float3 Blue[4] = { {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1} };
		
	if (curLevel < maxLevel)
	{
		BVHNode node = pBVHNode[nodeIdx];
		S_Box box;
		box.pmin = node.pmin;
		box.pmax = node.pmax;	

		int   axis  = node.Axis();

		if (axis != TREE_LEAF)
		{		
			// draw left
			int  lidx = nodeIdx+1;
			glRenderBVHTreeLeaf(pBVHNode, pVertex, pFace, pPrimitive, maxLevel, curLevel+1, lidx, empty);
			// draw right
			int  ridx = node.right;
			glRenderBVHTreeLeaf(pBVHNode, pVertex, pFace, pPrimitive, maxLevel, curLevel+1, ridx, empty);
		}
		else
		{
			BVHNode node = pBVHNode[nodeIdx];
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			if ((node.Count() != 0) || empty) 
			{
				int prim_start = node.start;
				int prim_count = node.Count();

				for (int i_prim = 0; i_prim < prim_count; i_prim++)
				{
					int     primitive = pPrimitive[i_prim + prim_start];
					int3    face = { pFace[3*primitive], pFace[3*primitive+1], pFace[3*primitive+2] };
					float4  v[3] = { pVertex[face.x],    pVertex[face.y],      pVertex[face.z] };					

					glRenderTriangle(v[0], v[1], v[2], f3(0, 1, 0));
				}
			}
			//glRender(box, (float3 *) Blue);
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		}
	}
	else
	{
		BVHNode node = pBVHNode[nodeIdx];
		S_Box box;
		box.pmin = node.pmin;
		box.pmax = node.pmax;	

		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		//if ((node.Count() != 0) || empty) glRender(box, (float3 *) Blue);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
}

void glRenderBVHTreeLeaf(CSpatialTree * pBVHTree, int maxLevel, bool empty)
{
	if (pBVHTree == NULL) return;

	glRenderBVHTreeLeaf((BVHNode *) pBVHTree->pBVHTree(), 
											 pBVHTree->pVertex(),
											 pBVHTree->pFace(),
											 pBVHTree->pPrimitive(),
											 maxLevel,
											 0, 0, 
											 empty) ;
}


void glRenderKdTreeRayTrace(CSpatialTree * pKd, float3 orig, float3 dir, int maxLevel, bool empty)
{
	KdNode * pNodes = (KdNode *) pKd->pKdTree();

	float4 *pVertex = pKd->pVertex();
	int32  *pFace   = pKd->pFace();
	uint32 *pIndex  = pKd->pPrimitive();

	KdNode node_stack[64];
	S_Box box_stack[64*2];
	S_Box lbox, rbox, *pnearbox, *pfarbox;
	S_Box box = pKd->box();

	float tnear = 0.0f, tfar = 0.0f, split = 0.0f, tsplit = 0.0f, thit = 3.402823466e+38F , tprev = thit;
	int stack_index = 0;
	int node_index = 0;
	int box_index = 0;
	int axis = TREE_LEAF;

	float3 rayD = dir;  //xyz(pRayD[iRay]);
	float3 rayO = orig; //xyz(pRayO[iRay]);

	bool bHit = true;

	float3 inv_dir = 1.0f / rayD;
	int    sign_dir[3] = { 0, 0, 0 };

	sign_dir[0] = (rayD.x >= 0) ? 1 : 0;
	sign_dir[1] = (rayD.y >= 0) ? 1 : 0;
	sign_dir[2] = (rayD.z >= 0) ? 1 : 0;

	if (Intersect(box.pmin, box.pmax, rayO, rayD, tnear, tfar))
	{
		if (tnear < 0) tnear = 0.0f;
		bHit = false;

		lbox = box;
		rbox = box;
	}

	while( !bHit )
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glRender(box, f3(0, 1, 0));
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

		split = pNodes[node_index].Split();
		axis  = pNodes[node_index].Axis();

		tsplit = (split - ((float*)&rayO)[axis]) * ((float*)&inv_dir)[axis];

		int first_left = sign_dir[axis];

		int near = 0;
		int far = 0;

		int left = node_index+1;
		int right = pNodes[node_index].next;			

		if (axis != TREE_LEAF)
		{
			lbox = box;
			rbox = box;

			((float*)&rbox.pmin)[axis] = split;
			((float*)&lbox.pmax)[axis] = split;
			if (first_left)
			{
				near = left;
				far = right;
				pnearbox = &lbox;
				pfarbox = &rbox;
			}
			else
			{
				near = right;					
				far = left;
				pnearbox = &rbox;
				pfarbox = &lbox;
			}

			if(tsplit < tnear)
			{
				node_index = far;
				box = *pfarbox;
			}
			else if (tsplit > tfar)
			{
				node_index = near;
				box = *pnearbox;
			}
			else
			{
				node_index = near;
				box = *pnearbox;

				node_stack[stack_index].t = tfar;
				node_stack[stack_index].next = far;
				box_stack[box_index] = *pfarbox;

				stack_index++;
				box_index++;

				tfar = tsplit;
			}
		}
		else
		{
			int32 prim_start = pNodes[node_index].start;
			int32 prim_count = pNodes[node_index].Count(); //(pNodes[node_index].count >> 2);

			for (int32 iPrimitive = 0; iPrimitive < prim_count; iPrimitive++)
			{
				int32 face_idx = pIndex[ prim_start + iPrimitive ];

				int3 face = i3( pFace[3*face_idx], pFace[3*face_idx+1], pFace[3*face_idx+2] );

				float3 v[3] = { xyz(pVertex[face.x]), xyz(pVertex[face.y]), xyz(pVertex[face.z]) };

				if (Intersect(v[0], v[1], v[2], rayO, rayD, thit))
				{					
					if (tprev > thit)
					{
						tprev = thit;					

						bHit = true;
					} // (tprev > thit)
				}  // if (Intersect(v[0], v[1], v[2], rayO, rayD, thit))
			} // for (int32 iPrimitive = 0; iPrimitive < prim_count; iPrimitive++)

			if (!bHit && stack_index > 0) 
			{				
				box_index--;
				stack_index--;

				tnear = tfar;
				tfar = node_stack[stack_index].t;
				node_index = node_stack[stack_index].next;

				box = box_stack[box_index];
			}
			else
			{
				bHit = true;
			}
		} //if (axis != KD_LEAF)
	} // while (!bhit)
}

void glRenderBVHTreeRayTrace(CSpatialTree * pBVH, float3 orig, float3 dir, int maxLevel, bool empty)
{
	float4 *pVertex = pBVH->pVertex();
	int32  *pFace   = pBVH->pFace();
	uint32 *pIndex  = pBVH->pPrimitive();

	BVHNode * pNodes = (BVHNode *) pBVH->pBVHTree();

	{
		int node_stack[64];

		float tnear = 0.0f, tfar = 0.0f, split = 0.0f, tsplit = 0.0f, thit = 3.402823466e+38F, tprev = thit;
		int stack_index = 0;
		int node_index = TREE_ROOT;

		float3 rayD = dir;
		float3 rayO = orig; 

		bool bHit = true;

		float3 inv_dir = 1.0f / rayD;
		int sign_dir[3] = { 0, 0, 0 }; 

		sign_dir[0] = (rayD.x >= 0) ? 1 : 0;
		sign_dir[1] = (rayD.y >= 0) ? 1 : 0;
		sign_dir[2] = (rayD.z >= 0) ? 1 : 0;

		BVHNode root = pNodes[node_index];

		float3 pmin = root.pmin;
		float3 pmax = root.pmax;

		S_Box box;

		if (Intersect(pmin, pmax, rayO, rayD, tnear, tfar))
		{
			box.pmax = pmax;
			box.pmin = pmin;
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			glRender(box, f3(0, 1, 0));
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

			if (tnear < 0) tnear = 0.0f;

			int axis = root.Axis();
			float split = root.Split();

			float o = ((float*)&rayO)[axis];
			float d = ((float*)&inv_dir)[axis];

			tsplit = (split - o) * d;

			int first_left = sign_dir[axis];
			int near, far;

			if (first_left) 
			{
				near = node_index+1;
				far = pNodes[node_index].right;
			}
			else
			{
				near = pNodes[node_index].right;
				far = node_index+1;
			}

			if(tsplit < tnear)
			{
				node_stack[stack_index++] = far;
			}
			else if (tsplit > tfar)
			{
				node_stack[stack_index++] = near;
			}
			else
			{
				node_stack[stack_index++] = near;
				node_stack[stack_index++] = far;
			}

			bHit = false;
		}

		while (!bHit && stack_index > 0)
		{
			stack_index--;

			node_index = node_stack[stack_index];
			BVHNode bvh = pNodes[node_index];
			
			pmin = bvh.pmin;
			pmax = bvh.pmax;

			if (Intersect(pmin, pmax, rayO, rayD, tnear, tfar))
			{
				box.pmax = pmax;
				box.pmin = pmin;
				glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
				glRender(box, f3(0, 1, 0));
				glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

				if (tnear < 0) tnear = 0.0f;

				if (bvh.Axis() != TREE_LEAF)
				{
					int axis = bvh.Axis();
					float split = bvh.Split();

					float o = ((float*)&rayO)[axis];
					float d = ((float*)&inv_dir)[axis];

					tsplit = (split - o) * d;

					int first_left = sign_dir[axis];
					int near, far;

					if (first_left) 
					{
						near = node_index+1;
						far = pNodes[node_index].right;
					}
					else
					{
						near = pNodes[node_index].right;
						far = node_index+1;
					}

					if(tsplit < tnear)
					{
						node_stack[stack_index++] = far;
					}
					else if (tsplit > tfar)
					{
						node_stack[stack_index++] = near;
					}
					else
					{
						node_stack[stack_index++] = near;
						node_stack[stack_index++] = far;
					}
				}
				else
				{
					int32 prim_start = bvh.start;
					int32 prim_count = bvh.Count();

					for (int32 iPrimitive = 0; iPrimitive < prim_count; iPrimitive++)
					{
						int32 face_idx = pIndex[ prim_start + iPrimitive ];

						int3 face = i3( pFace[3*face_idx], pFace[3*face_idx+1], pFace[3*face_idx+2] );

						float3 v[3] = { xyz(pVertex[face.x]), xyz(pVertex[face.y]), xyz(pVertex[face.z]) };

						if (Intersect(v[0], v[1], v[2], rayO, rayD, thit))
						{					
							if (tprev > thit)
							{										
								tprev = thit;					

								bHit = true;
							} // if (tprev > thit)
						} // if (Intersect(v[0], v[1], v[2], rayO, rayD, thit))
					}	// for (int32 iPrimitive = 0; iPrimitive < prim_count; iPrimitive++)	 			
				} // else
			} // if (Intersect(pmin, pmax, rayO, rayD, tnear, tfar))
		} // while (!bHit && stack_index > 0)
	} // if (Intersect(pmin, pmax, rayO, rayD, tnear, tfar)) (ROOT NODE)
}